// Copyright 2009-2021 NTESS. Under the terms
// of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Copyright (c) 2009-2021, NTESS
// All rights reserved.
//
// Portions are copyright of other developers:
// See the file CONTRIBUTORS.TXT in the top level directory
// the distribution for more information.
//
// This file is part of the SST software package. For license
// information, see the LICENSE file in the top level directory of the
// distribution.

#ifndef _H_VANADIS_ISA_TABLE
#define _H_VANADIS_ISA_TABLE

#include "decoder/visaopts.h"

namespace SST {
namespace Vanadis {

class VanadisISATable {
public:
	VanadisISATable(
			const VanadisDecoderOptions* decoder_o,
			const uint16_t int_reg,
			const uint16_t fp_reg ) :
		decoder_opts(decoder_o),
		count_int_reg(int_reg),
		count_fp_reg(fp_reg) {

		int_reg_ptr = new uint16_t[int_reg];
		fp_reg_ptr  = new uint16_t[fp_reg];

		int_reg_pending_read = new uint32_t[int_reg];
		fp_reg_pending_read = new uint32_t[fp_reg];

		int_reg_pending_write = new uint32_t[int_reg];
		fp_reg_pending_write = new uint32_t[fp_reg];

		for( uint16_t i = 0 ; i < int_reg; ++i ) {
			int_reg_ptr[i] = 0;
			int_reg_pending_read[i] = 0;
			int_reg_pending_write[i] = 0;
		}

		for( uint16_t i = 0; i < fp_reg; ++i ) {
			fp_reg_ptr[i] = 0;
			fp_reg_pending_read[i] = 0;
			fp_reg_pending_write[i] = 0;
		}
	}

	~VanadisISATable() {
		delete int_reg_ptr;
		delete int_reg_pending_read;
		delete int_reg_pending_write;
		delete fp_reg_ptr;
		delete fp_reg_pending_read;
		delete fp_reg_pending_write;
	}

	bool pendingIntReads( const uint16_t int_reg ) {
		return int_reg_pending_read[int_reg] > 0;
	}

	bool pendingIntWrites( const uint16_t int_reg ) {
		return int_reg_pending_write[int_reg] > 0;
	}

	bool pendingFPReads( const uint16_t fp_reg ) {
		return fp_reg_pending_read[fp_reg] > 0;
	}

	bool pendingFPWrites( const uint16_t fp_reg ) {
		return fp_reg_pending_write[fp_reg] > 0;
	}

	void incIntRead( const uint16_t int_reg ) {
		if( int_reg != decoder_opts->getRegisterIgnoreWrites() ) {
			int_reg_pending_read[int_reg]++;
		}
	}

	void incIntWrite( const uint16_t int_reg ) {
		if( int_reg != decoder_opts->getRegisterIgnoreWrites() ) {
			int_reg_pending_write[int_reg]++;
		}
	}

	void decIntRead( const uint16_t int_reg ) {
		if( int_reg != decoder_opts->getRegisterIgnoreWrites() ) {
			int_reg_pending_read[int_reg]--;
		}
	}

	void decIntWrite( const uint16_t int_reg ) {
		if( int_reg != decoder_opts->getRegisterIgnoreWrites() ) {
			int_reg_pending_write[int_reg]--;
		}
	}

	void incFPRead( const uint16_t fp_reg ) {
		fp_reg_pending_read[fp_reg]++;
	}

	void incFPWrite( const uint16_t fp_reg ) {
		fp_reg_pending_write[fp_reg]++;
	}

	void decFPRead( const uint16_t fp_reg ) {
		fp_reg_pending_read[fp_reg]--;
	}

	void decFPWrite( const uint16_t fp_reg ) {
		fp_reg_pending_write[fp_reg]--;
	}

	void setIntPhysReg( const uint16_t int_reg, const uint16_t phys_reg ) {
		int_reg_ptr[int_reg] = phys_reg;
	}

	void setFPPhysReg( const uint16_t fp_reg, const uint16_t phys_reg ) {
		fp_reg_ptr[fp_reg] = phys_reg;
	}

	uint16_t getIntPhysReg( const uint16_t int_reg ) {
		return int_reg_ptr[int_reg];
	}

	uint16_t getFPPhysReg( const uint16_t fp_reg ) {
		return fp_reg_ptr[fp_reg];
	}

	void reset( VanadisISATable* tbl ) {
		for(uint16_t i = 0; i < count_int_reg; ++i) {
			int_reg_ptr[i] = tbl->int_reg_ptr[i];
			int_reg_pending_read[i] = tbl->int_reg_pending_read[i];
			int_reg_pending_write[i] = tbl->int_reg_pending_write[i];
		}

		for(uint16_t i = 0; i < count_fp_reg; ++i) {
			fp_reg_ptr[i] = tbl->fp_reg_ptr[i];
			fp_reg_pending_read[i] = tbl->fp_reg_pending_read[i];
			fp_reg_pending_write[i] = tbl->fp_reg_pending_write[i];
		}
	}

	void print(SST::Output* output, VanadisRegisterFile* regFile, bool print_int, bool print_fp) {
		print(output, regFile, print_int, print_fp, 16);
	}

	void print(SST::Output* output, VanadisRegisterFile* regFile, bool print_int, bool print_fp, uint32_t output_v ) {
		//char* reg_bin_str = new char[65];

		if( print_int ) {
			output->verbose(CALL_INFO, output_v, 0, "Integer Registers (Count=%" PRIu16 ")\n", count_int_reg);
			for( uint16_t i = 0; i < count_int_reg; ++i ) {
				if( nullptr == regFile ) {
					output->verbose(CALL_INFO, output_v, 0, "| isa:%5" PRIu16 " -> phys:%5" PRIu16 " | r:%6" PRIu32 " | w:%6" PRIu32 " |\n",
						i, int_reg_ptr[i], int_reg_pending_read[i], int_reg_pending_write[i]);
				} else {
					int64_t* val = (int64_t*) regFile->getIntReg( i );
					//toBinaryString(reg_bin_str, *val);

					output->verbose(CALL_INFO, output_v, 0, "| isa:%5" PRIu16 " -> phys:%5" PRIu16 " | r:%5" PRIu32 " | w:%5" PRIu32 " | v: 0x%016llx | v: %" PRIu64 " / %" PRId64 "\n",
						i, int_reg_ptr[i], int_reg_pending_read[i], int_reg_pending_write[i],
						regFile->getIntReg<int64_t>( int_reg_ptr[i] ),
						regFile->getIntReg<uint64_t>( int_reg_ptr[i] ),
						regFile->getIntReg<int64_t>( int_reg_ptr[i] ) );
				}
			}
		}

		if( print_fp ) {

		output->verbose(CALL_INFO, output_v, 0, "Floating-Point Registers (Count=%" PRIu16 ")\n", count_fp_reg);
			for( uint16_t i = 0; i < count_fp_reg; ++i ) {
				if( nullptr == regFile ) {
					output->verbose(CALL_INFO, output_v, 0, "| isa:%5" PRIu16 " -> phys:%5" PRIu16 " | r:%5" PRIu32 " | w:%5" PRIu32 " |\n",
						i, fp_reg_ptr[i], fp_reg_pending_read[i], fp_reg_pending_write[i]);
				} else {
					if( VANADIS_REGISTER_MODE_FP32 ==  decoder_opts->getFPRegisterMode() ) {
						output->verbose(CALL_INFO, output_v, 0, "| isa:%5" PRIu16 " -> phys:%5" PRIu16 " | r:%5" PRIu32 " | w:%5" PRIu32 " | v: 0x%016llx |\n",
							i, fp_reg_ptr[i], fp_reg_pending_read[i], fp_reg_pending_write[i],
							(uint64_t) regFile->getFPReg<uint32_t>( fp_reg_ptr[i] ) );
					} else {
						output->verbose(CALL_INFO, output_v, 0, "| isa:%5" PRIu16 " -> phys:%5" PRIu16 " | r:%5" PRIu32 " | w:%5" PRIu32 " | v: 0x%016llx |\n",
							i, fp_reg_ptr[i], fp_reg_pending_read[i], fp_reg_pending_write[i],
							regFile->getFPReg<uint64_t>( fp_reg_ptr[i] ) );
					}
				}
			}
		}

		//delete[] reg_bin_str;
	}

	void print(SST::Output* output, bool print_int, bool print_fp ) {
		print(output, nullptr, print_int, print_fp);
	}

	void toBinaryString(char* buffer, int64_t val) {
		int index = 0;

		for( uint64_t i = 1L << 63L; i > 0; i = i / 2) {
			buffer[index++] = (val & i) ? '1' : '0';
		}

		buffer[index] = '\0';
	}

	bool physIntRegInUse( const uint16_t reg ) {
		return findInRegSet( reg, int_reg_ptr, count_int_reg );
	}

	bool physFPRegInUse( const uint16_t reg ) {
		return findInRegSet( reg, fp_reg_ptr, count_fp_reg );
	}

protected:

	bool findInRegSet( const uint16_t reg, const uint16_t* reg_set, const uint16_t reg_count ) const {
		bool found = false;

		for( uint16_t i = 0; i < reg_count; ++i ) {
			if( reg == reg_set[i] ) {
				found = true;
			}
		}

		return found;
	}

	const uint16_t count_int_reg;
	const uint16_t count_fp_reg;

	uint16_t* int_reg_ptr;
	uint16_t* fp_reg_ptr;

	uint32_t* int_reg_pending_read;
	uint32_t* fp_reg_pending_read;

	uint32_t* int_reg_pending_write;
	uint32_t* fp_reg_pending_write;

	const VanadisDecoderOptions* decoder_opts;
};

}
}

#endif
